// 开闭原则
/**
 * open for extension, closed for modification
 * 避免修改，而是扩展
 */

// todo: what is freeze : 静态方法，防止对象被修改
let Color = Object.freeze({
    red: 'red',
    green: 'green',
    blue: 'blue'
})

let Size = Object.freeze({
    small: 'small',
    medium: 'medium',
    large: 'large'
})
// 产品类
class Product {

    constructor(name, color, size) {
        this.name = name;
        this.color = color;
        this.size = size;
    }
}

// 产品过滤类，用于过滤不同产品
class ProductFilter {

    // 按颜色过滤
    filterByColor(products, color) {
        return products.filter(product => product.color === color)
    }

    // 按大小过滤
    filterBySize(products, size) {
        return products.filter(product => product.size === size)
    }

    // 按颜色和大小
    filterByColorAndSize(products, color, size) {
        return products.filter(product => 
            product.color === color && product.size === size);
    }
    
    // 名字过滤、大小和名字、大小和颜色和名字...
}
/**
 *  如果按照这种写法，每个属性都写一个，3个属性一共需要实现7种方法 （c31 + c32 + c33 = 7）
 *  
 * 
 *  解决/应用：
 *  当有一个特定的过滤标准时，考虑指定一个单独的类，来定义这种过滤方式 ===》 specification 规范 类似JAVA里面的抽象类
 */
   
class ColorSpecification {
    constructor(color) {
        this.color = color;
    }

    isSatisfied(item) {
        return item.color === this.color;
    }
}

class SizeSpecification {
    constructor(size) {
        this.size = size;
    }

    isSatisfied(item) {
        return item.size === this.size;
    }
}

// 复合
class AndSpecification {
    constructor(...specs) {
        console.log('*****初始化过滤器******');
        console.log(...specs);
        this.specs = specs;
    }

    isSatisfied(item) {
        return this.specs.every(x => x.isSatisfied(item))
    }
}

class BetterFilter {
    filter(items, spec) {
        return items.filter(x => spec.isSatisfied(x));
    }
}

let apple = new Product('apple', Color.red, Size.small);
let tree = new Product('tree', Color.green, Size.medium);
let house = new Product('house', Color.blue, Size.large);
let elephant = new Product('elephant', Color.blue, Size.large)

let products = [apple, tree, house, elephant];
let pf = new ProductFilter();
console.log(`Green products (old):`);
for (let p of pf.filterByColor(products, Color.green)) {
    console.log(`* ${p.name} is green`);
}

let bf = new BetterFilter();
console.log(`Green products (new): `);
for (const p of bf.filter(products,
    new ColorSpecification(Color.green))) {
    console.log(`* ${p.name} is green`);
}

// 复合
let spec = new AndSpecification(
    new ColorSpecification(Color.blue),
    new SizeSpecification(Size.large)
)

console.log(`Blue and Large products (new): `);
for (const p of bf.filter(products, spec)) {
    console.log(p.name);
}